package com.ikingtech.platform.service.system.role.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.context.event.TenantDeleteEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.role.api.RoleApi;
import com.ikingtech.framework.sdk.role.api.RoleUserApi;
import com.ikingtech.framework.sdk.role.model.RoleDTO;
import com.ikingtech.framework.sdk.role.model.RoleMenuUpdateParamDTO;
import com.ikingtech.framework.sdk.role.model.RoleQueryParamDTO;
import com.ikingtech.framework.sdk.role.model.RoleUserUpdateParamDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.role.entity.RoleDO;
import com.ikingtech.platform.service.system.role.entity.RoleDataScopeDO;
import com.ikingtech.platform.service.system.role.entity.RoleMenuDO;
import com.ikingtech.platform.service.system.role.exception.RoleExceptionInfo;
import com.ikingtech.platform.service.system.role.service.repository.ModelConverter;
import com.ikingtech.platform.service.system.role.service.repository.RoleDataScopeRepository;
import com.ikingtech.platform.service.system.role.service.repository.RoleMenuRepository;
import com.ikingtech.platform.service.system.role.service.repository.RoleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
@ApiController(value = "/system/role", name = "系统管理-角色管理", description = "系统管理-角色管理")
public class RoleController implements RoleApi {

    private final RoleRepository repo;

    private final RoleDataScopeRepository dataScopeRepo;

    private final RoleMenuRepository roleMenuRepo;

    private final RoleUserApi roleUserApi;

    private final ModelConverter converter;

    /**
     * 添加角色
     *
     * @param role 角色信息
     * @return 返回添加成功后的角色ID
     */
    @Override
    @OperationLog(value = "新增角色", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(RoleDTO role) {
        if (this.repo.exists(Wrappers.<RoleDO>lambdaQuery().eq(RoleDO::getName, role.getName()).eq(Tools.Str.isNotBlank(Me.tenantCode()), RoleDO::getTenantCode, Me.tenantCode()))) {
            throw new FrameworkException(RoleExceptionInfo.DUPLICATE_ROLE_NAME);
        }
        // 将DTO对象转换为DO对象
        RoleDO entity = Tools.Bean.copy(role, RoleDO.class);
        // 生成角色ID
        entity.setId(Tools.Id.uuid());
        // 设置角色排序顺序
        entity.setSortOrder(this.getMaxSortOrder() + 1);
        // 设置角色所属领域、租户和应用标识
        entity.setDomainCode(Me.domainCode());
        entity.setTenantCode(Me.tenantCode());
        entity.setAppCode(Me.appCode());
        // 保存角色信息
        this.repo.save(entity);
        // 返回添加成功后的角色ID
        this.insertDataScopeCode(entity.getId(), role.getDataScopeCodes());
        return R.ok(entity.getId());
    }

    /**
     * 删除角色
     *
     * @param id 角色ID
     * @return 删除结果
     */
    @Override
    @OperationLog(value = "删除角色")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        // 删除角色
        this.repo.removeById(id);
        // 删除角色的数据范围
        this.dataScopeRepo.remove(Wrappers.<RoleDataScopeDO>lambdaQuery().eq(RoleDataScopeDO::getRoleId, id));
        // 删除角色的菜单权限
        this.roleMenuRepo.remove(Wrappers.<RoleMenuDO>lambdaQuery().eq(RoleMenuDO::getRoleId, id).eq(RoleMenuDO::getTenantCode, Me.tenantCode()));
        // 删除角色的用户权限
        this.roleUserApi.removeUserRole(id);
        return R.ok();
    }

    /**
     * 更新角色
     *
     * @param role 角色信息
     * @return 更新结果
     */
    @Override
    @OperationLog(value = "更新角色")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(RoleDTO role) {
        if (this.repo.exists(Wrappers.<RoleDO>lambdaQuery().ne(RoleDO::getId, role.getId()).eq(RoleDO::getName, role.getName()).eq(Tools.Str.isNotBlank(Me.tenantCode()), RoleDO::getTenantCode, Me.tenantCode()))) {
            throw new FrameworkException(RoleExceptionInfo.DUPLICATE_ROLE_NAME);
        }
        // 检查角色是否存在
        if (!this.repo.exists(Wrappers.<RoleDO>lambdaQuery().eq(RoleDO::getId, role.getId()))) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        // 更新角色信息
        this.repo.updateById(Tools.Bean.copy(role, RoleDO.class));
        this.updateDataScopeCode(role.getId(), role.getDataScopeCodes());
        return R.ok();
    }

    /**
     * 分页查询角色信息
     *
     * @param queryParam 查询参数
     * @return 返回角色信息列表
     */
    @Override
    public R<List<RoleDTO>> page(RoleQueryParamDTO queryParam) {
        return R.ok(PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()), RoleRepository.createWrapper(queryParam, Me.tenantCode()))).convertBatch(this.converter::modelConvert));
    }

    /**
     * 获取所有角色信息
     *
     * @return 返回角色列表
     */
    @Override
    public R<List<RoleDTO>> all() {
        // 使用lambdaQuery方法构建查询条件
        return R.ok(this.converter.modelConvert(this.repo.list(Wrappers.<RoleDO>lambdaQuery()
                .eq(Tools.Str.isNotBlank(Me.domainCode()), RoleDO::getDomainCode, Me.domainCode())
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), RoleDO::getTenantCode, Me.tenantCode())
                .eq(Tools.Str.isNotBlank(Me.appCode()), RoleDO::getAppCode, Me.appCode())
                .orderByDesc(RoleDO::getCreateTime))));
    }

    /**
     * 获取角色详情
     * @param id 角色ID
     * @return 角色详情
     */
    @Override
    public R<RoleDTO> detail(String id) {
        // 根据ID查询角色实体
        RoleDO entity = this.repo.getById(id);
        // 如果查询结果为空，则抛出异常
        if (null == entity) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        // 构建角色详情
        return R.ok(this.converter.modelConvert(entity, this.dataScopeRepo.listObjs(Wrappers.<RoleDataScopeDO>lambdaQuery()
                .select(RoleDataScopeDO::getDataScopeCode)
                .eq(RoleDataScopeDO::getRoleId, entity.getId()))));
    }

    /**
     * 更新角色菜单
     * @param roleMenu 角色菜单参数
     * @return 返回操作结果
     */
    @Override
    @OperationLog(value = "更新角色菜单")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> updateRoleMenu(RoleMenuUpdateParamDTO roleMenu) {
        // 删除角色对应的菜单
        this.roleMenuRepo.remove(Wrappers.<RoleMenuDO>lambdaQuery().eq(RoleMenuDO::getRoleId, roleMenu.getRoleId()).eq(RoleMenuDO::getTenantCode, Me.tenantCode()));

        // 如果传入了菜单ID列表，则保存角色对应的菜单
        if (Tools.Coll.isNotBlank(roleMenu.getMenuIds())) {
            this.roleMenuRepo.saveBatch(Tools.Coll.convertList(roleMenu.getMenuIds(), menuId -> {
                RoleMenuDO entity = new RoleMenuDO();
                entity.setId(Tools.Id.uuid());
                entity.setRoleId(roleMenu.getRoleId());
                entity.setMenuId(menuId);
                entity.setDomainCode(Me.domainCode());
                entity.setTenantCode(Me.tenantCode());
                entity.setAppCode(Me.appCode());
                return entity;
            }));
        }

        return R.ok();
    }

    /**
     * 分配角色用户
     * @param updateParam 更新参数
     * @return 返回结果
     */
    @Override
    @OperationLog(value = "分配角色用户")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> addRoleUser(RoleUserUpdateParamDTO updateParam) {
        // 检查角色是否存在
        if (!this.repo.exists(Wrappers.<RoleDO>lambdaQuery().eq(RoleDO::getId, updateParam.getRoleId()))) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        // 绑定角色和用户的关系
        this.roleUserApi.bindUserRole(updateParam.getRoleId(), updateParam.getUserIds());
        return R.ok();
    }

    /**
     * 移除角色用户
     * @param updateParam 更新参数
     * @return 操作结果
     */
    @Override
    @OperationLog(value = "移除角色用户")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> removeRoleUser(RoleUserUpdateParamDTO updateParam) {
        // 检查角色是否存在
        if (!this.repo.exists(Wrappers.<RoleDO>lambdaQuery().eq(RoleDO::getId, updateParam.getRoleId()))) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        // 解绑角色用户
        this.roleUserApi.unbindUserRole(updateParam.getRoleId(), updateParam.getUserIds());
        return R.ok();
    }

    /**
     * 处理租户删除事件的方法
     * @param event 租户删除事件对象
     */
    @EventListener
    public void tenantDeleteEventListener(TenantDeleteEvent event) {
        // 根据租户代码查询角色并删除
        this.repo.remove(Wrappers.<RoleDO>query().lambda().eq(RoleDO::getTenantCode, event.getCode()));
        // 根据租户代码查询角色菜单并删除
        this.roleMenuRepo.remove(Wrappers.<RoleMenuDO>lambdaQuery().eq(RoleMenuDO::getTenantCode, event.getCode()));
    }

    private Integer getMaxSortOrder() {
        List<Number> orders = this.repo.listObjs(Wrappers.<RoleDO>query().lambda()
                .select(RoleDO::getSortOrder)
                .eq(RoleDO::getTenantCode, Me.tenantCode())
                .orderByDesc(RoleDO::getSortOrder));
        return Tools.Coll.isBlank(orders) ? 0 : orders.get(0).intValue();
    }

    private void insertDataScopeCode(String roleId, List<String> dataScopeCodes) {
        if (Tools.Coll.isNotBlank(dataScopeCodes)) {
            this.dataScopeRepo.saveBatch(Tools.Coll.convertList(dataScopeCodes, dataScopeCode -> {
                RoleDataScopeDO entity = new RoleDataScopeDO();
                entity.setId(Tools.Id.uuid());
                entity.setRoleId(roleId);
                entity.setDataScopeCode(dataScopeCode);
                entity.setTenantCode(Me.tenantCode());
                return entity;
            }));
        }
    }

    private void updateDataScopeCode(String roleId, List<String> dataScopeCodes) {
        this.dataScopeRepo.remove(Wrappers.<RoleDataScopeDO>lambdaQuery().eq(RoleDataScopeDO::getRoleId, roleId));
        this.insertDataScopeCode(roleId, dataScopeCodes);
    }
}
